package com.dries.portal.module.coupon.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dries.common.constant.Constant;
import com.dries.portal.module.coupon.enums.CouponStatus;
import com.dries.common.exception.ApiException;
import com.dries.portal.module.coupon.entity.Coupon;
import com.dries.portal.module.coupon.entity.Template;
import com.dries.portal.module.coupon.form.AcquireTemplateForm;
import com.dries.portal.module.coupon.mapper.CouponMapper;
import com.dries.portal.module.coupon.service.ICouponService;
import com.dries.portal.module.coupon.service.IRedisService;
import com.dries.portal.module.coupon.service.ITemplateService;
import com.dries.portal.module.coupon.utils.convert.Coupon2CouponVoConvert;
import com.dries.portal.module.coupon.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.util.Pair;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {

    @Resource
    private CouponMapper couponMapper;
    @Resource
    private ITemplateService templateService;
    @Resource
    private IRedisService iRedisService;
    @Resource
    private RabbitTemplate rabbitTemplate;

    /**
     * 根据 userId + 状态 寻找优惠券记录
     *
     * @param userId
     * @param code
     */
    @Override
    public List<CouponVo> findAllByUserIdAndStatus(Long userId, Integer code) {

        List<Coupon> list = new LambdaQueryChainWrapper<>(couponMapper)
                .eq(Coupon::getUserId, userId)
                .eq(Coupon::getStatus, CouponStatus.of(code))
                .list();

        if (CollectionUtil.isEmpty(list)) {
            return new ArrayList<>();
        }

        return list.stream().map(e -> {
            log.info("获取的用户优惠券信息=================》 {}", JSONUtil.toJsonStr(e));
            Template template = templateService.getTemplateInfoById(e.getTemplateId());
            if (template != null) {
                return Coupon2CouponVoConvert.convert(e, template);
            }
            return null;
        }).filter(Objects::nonNull).collect(Collectors.toList());
    }

    /**
     * 根据用户 id 和状态查询优惠券记录
     *
     * @param userId
     * @param status
     */
    @Override
    public List<CouponVo> findCouponsByStatus(Long userId, Integer status) {
        List<CouponVo> curCached = iRedisService.getCacheCoupons(userId, status);
        // log.info("获取缓存中的优惠券信息=======================》{}", curCached);
        List<CouponVo> preTarget;

        if (!CollectionUtils.isEmpty(curCached)) {
            log.info("coupon cache is not empty: {}, {}", userId, status);
            preTarget = curCached;
        } else {
            // 从数据库中获取优惠券信息
            log.info("coupon cache is empty,get coupon form db {}, {}", userId, status);
            List<CouponVo> dbCoupons = findAllByUserIdAndStatus(userId, status);
            if (CollectionUtils.isEmpty(dbCoupons)) {
                log.info("当前用户暂未领取优惠券");
                return dbCoupons;
            }
            // 填充 dbCoupons 的 templateSdk字段
            // 数据库中存在记录
            preTarget = dbCoupons;
            // 将记录写入Cache
            iRedisService.addCouponToCache(userId, preTarget, status);
        }
        // 将无效优惠券踢出
        preTarget = preTarget.stream()
                .filter(e -> e.getId() != -1)
                .collect(Collectors.toList());
        //        log.info("获取有效的优惠券列表： {}", preTarget);
        if (CouponStatus.of(status) == CouponStatus.USABLE) {
            CouponClassify classify = CouponClassify.classify(preTarget);
            // 如果已过期状态不为空，需要做延迟处理
            if (!CollectionUtils.isEmpty(classify.getExpired())) {
                iRedisService.addCouponToCache(userId, classify.getExpired(), CouponStatus.EXPIRED.getCode());
                // TODO
                // 发送到rabbitmq
                rabbitTemplate.convertAndSend(
                        Constant.TOPIC_COUPON,
                        "coupon.news",
                        JSONUtil.toJsonStr(new CouponRabbitMqMessage(
                                CouponStatus.EXPIRED.getCode(),
                                classify.getExpired().stream()
                                        .map(CouponVo::getId)
                                        .collect(Collectors.toList())
                        )));
            }
            return classify.getUsable();
        }
        return preTarget;
    }

    /**
     * 根据用户ID查找当前可以领取的优惠券模板
     *
     * @param userId
     */
    @Override
    public List<CouponTemplateSDK> findAvailableCouponTemplate(Long userId) {
        // 获取当前时间戳
        long curTime = System.currentTimeMillis();
        // 获取所有可用的优惠券模板
        List<CouponTemplateSDK> allUsableTemplate = templateService.findAllUsableTemplate();
        // 过滤过期的优惠券模板 和 下架的优惠券模板
        allUsableTemplate = allUsableTemplate.stream().filter(e -> e.getRule().getExpiration().getDeadline().before(new Date())).collect(Collectors.toList());
        // key 是 TemplateId
        // value 中的key 是 Template limitation，right 是优惠券模板
        Map<Integer, Pair<Integer, CouponTemplateSDK>> limit2Template = new HashMap<>();
        allUsableTemplate.forEach(e -> limit2Template.put(
                e.getId(),
                Pair.of(e.getRule().getLimitation(), e))
        );
        // 返回用户可以领取的优惠券 result
        List<CouponTemplateSDK> result = new ArrayList<>();
        // 获取用户当前可用的优惠券列表信息
        List<CouponVo> userUsableCoupons = findCouponsByStatus(userId, CouponStatus.USABLE.getCode());
        log.info("当前用户可用的优惠券：{}, {}", userId, userUsableCoupons.size());
        // key 是 TemplateId
        Map<Integer, List<CouponVo>> temIds2Coupon = userUsableCoupons
                .stream().collect(Collectors.groupingBy(CouponVo::getTemplateId));
        // 根据Template 的 Rule 判断是否可以领取优惠券模板
        limit2Template.forEach((k, v) -> {
            int limitation = v.getFirst();
            CouponTemplateSDK sdk = v.getSecond();
            if (temIds2Coupon.containsKey(k) && temIds2Coupon.get(k).size() >= limitation) {
                log.info("优惠券模板 {} 超过最大领取数量", sdk.getName());
            } else {
                result.add(sdk);
            }
        });
        return result;
    }

    /**
     * 用户领取优惠券
     * 1. 拿到对应优惠券，并检查是否过期
     * 2.根据limitation 判断是否可以领取
     * 3. 保存到数据库
     * 4. 保存到缓存
     *
     * @param form
     */
    @Override
    public CouponVo acquireTemplate(AcquireTemplateForm form) {
        Map<Integer, CouponTemplateSDK> idsTemplateSDK = templateService.findIdsTemplateSDK(Collections.singletonList(form.getTemplateId()));
        CouponTemplateSDK curTemplateSDK = idsTemplateSDK.get(form.getTemplateId());
        // 优惠券模板是需要存在的
        if (idsTemplateSDK.size() <= 0) {
            throw new ApiException("没有可用的优惠券");
        }
        // 用户是否可以领取这张优惠券
        List<CouponVo> userUsableCoupons = findCouponsByStatus(form.getUserId(), CouponStatus.USABLE.getCode());

        if (CollUtil.isNotEmpty(userUsableCoupons)) {
            Map<Integer, List<CouponVo>> temId2Coupons = userUsableCoupons.stream()
                    .collect(Collectors.groupingBy(CouponVo::getTemplateId));
            if (temId2Coupons.containsKey(form.getTemplateId()) &&
                    temId2Coupons.get(form.getTemplateId()).size() >=
                            curTemplateSDK.getRule().getLimitation()) {
                log.info("优惠券领取数量限制：{}",form.getTemplateId());
                throw new ApiException("该优惠券已领取");
            }
            long curTime = System.currentTimeMillis();
            if (curTemplateSDK.getRule().getExpiration().getDeadline().compareTo(new Date()) < 0) {
                throw new ApiException("优惠券已过期");
            }
        }

        // 尝试获取优惠券码
        String couponCode = iRedisService.tryToAcquireCouponCodeFromCache(form.getTemplateId());
        if (StrUtil.isEmpty(couponCode)) {
            log.info("优惠券已经被领取完了：{}",form.getTemplateId());
            throw new ApiException("优惠券已经被领取完了");
        }
        Coupon coupon = new Coupon(form.getTemplateId(), form.getUserId(), couponCode, CouponStatus.USABLE);

        boolean save = save(coupon);
        // 填充Coupon对象的CouponTemplateSDK，一定要放在缓存之前去填充
        if (!save) {
            throw new ApiException("服务器内部错误");
        }
        CouponVo couponVo = Coupon2CouponVoConvert.convertSDK(coupon, curTemplateSDK);
        // 放入缓存中
        iRedisService.addCouponToCache(form.getUserId(), Collections.singletonList(couponVo), CouponStatus.USABLE.getCode());
        return couponVo;
    }

    /**
     * 结算（核销）优惠券
     *
     * @param info
     */
    @Override
    public SettlementInfo settlementInfo(SettlementInfo info) {
        return null;
    }
}